﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
using Google.Analytics.BusinessLogic;
using Google.Analytics.Common;
using Google.Analytics.Model;
using Google.Analytics.Model.Queries;
using Google.Analytics.Web.Converters;
using Microsoft.Practices.Unity;

namespace Google.Analytics.Web
{
    /// <summary>
    /// Chart containing Google Analytics data based on <see cref="Metrics"/> and <see cref="Dimensions"/>
    /// </summary>
    public class AnalyticsGraph : Chart
    {
        #region UnityContainer

        private UnityContainer analyticsContainer;
        /// <summary>
        /// Gets the unity container to resove the 
        /// </summary>
        public UnityContainer AnalyticsContainer
        {
            get
            {
                if (analyticsContainer == null)
                {
                    analyticsContainer = (UnityContainer)Page.Application["Google.Analytics"];
                    if (analyticsContainer == null) throw new AnalyticsException(ExceptionText.InvalidUnityContainer);
                }
                return analyticsContainer;
            }
        }
        #endregion

        #region Public Properties

        private const string authKey = "authKey";

        /// <summary>
        /// Authentication details using <see cref="AuthState"/> object
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public AuthState Authentication
        {
            get { return (AuthState)ViewState[authKey]; }
            set { ViewState[authKey] = value; }
        }

        /// <summary>
        /// Gets or Sets the username
        /// </summary>
        public string AccountID { get; set; }

        /// <summary>
        /// Gets or Sets the Metrics for the query
        /// </summary>
        [TypeConverterAttribute(typeof(EnumConverter<Metrics>))]
        public Metrics[] Metrics { get; set; }

        /// <summary>
        /// Gets or Sets the dimensions for the query
        /// </summary>
        [TypeConverterAttribute(typeof(EnumConverter<Dimensions>))]
        public Dimensions[] Dimensions { get; set; }

        /// <summary>
        /// Gets or Sets the order of the query
        /// </summary>
        [TypeConverter(typeof(SortConverter))]
        public Order[] Order { get; set; }

        /// <summary>
        /// Gets or Sets the filter
        /// </summary>
        public string Filter { get; set; }

        private DateTime startDate = DateTime.Now.AddMonths(-1);
        /// <summary>
        /// Gets or Sets the start date for the query
        /// </summary>
        public DateTime StartDate
        {
            get { return startDate; }
            set { startDate = value; }
        }

        private DateTime endDate = DateTime.Now;
        /// <summary>
        /// Gets or Sets the end date for the query
        /// </summary>
        public DateTime EndDate
        {
            get { return endDate; }
            set { endDate = value; }
        }
        #endregion

        /// <summary>
        /// DataBind override
        /// </summary>
        public override void DataBind()
        {

            //populate the graph based on the data
            var auth = Authentication;
            var manager = AnalyticsContainer.Resolve<DataManager>();
            var data = manager.GetData(ref auth, AccountID, Dimensions, Metrics, Filter,
                            Order, StartDate, EndDate);
            Authentication = auth;


            //check if any data is returned

            for (int i = 0; i < data.Count; i++)
            {
                var entry = data[i];
                var x = (entry.Dimensions == null || entry.Dimensions.Count == 0) ? null : entry.Dimensions[0].Value;
                //ensure there is enough series
                if (Series.Count < entry.Metrics.Count)
                {
                    for (int j = 0; j < (entry.Metrics.Count - Series.Count); j++)
                        Series.Add(CreateSerie());
                }

                for (int j = 0; j < entry.Metrics.Count; j++)
                {
                    var metric = entry.Metrics[j];
                    var ser = GetSerie(j);
                    ser.Points.AddXY(x ?? metric.Name, metric.Value);
                }
            }

            base.DataBind();
        }

        #region Helpers
        private Series CreateSerie()
        {
            var serie = new Series();
            Color color = GetRandomColor();
            serie.Color = color;
            serie.BorderColor = color;
            serie.ChartType = (Series[0] == null) ? SeriesChartType.Bar : Series[0].ChartType;
            return serie;
        }

        private Series GetSerie(int i)
        {
            bool singleSeries = false;
            var ser = Series[0];
            var singleTypes = new List<SeriesChartType> { SeriesChartType.Pie, SeriesChartType.Doughnut, SeriesChartType.Polar, SeriesChartType.Pyramid, SeriesChartType.Funnel };
            foreach (var type in singleTypes)
                if (type == ser.ChartType) singleSeries = true;

            return (singleSeries) ? Series[0] : Series[i];
        }

        private static Color GetRandomColor()
        {
            var rnd = new Random();
            return Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
        }
        #endregion
    }
}
